package org.persistente.xml;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 *
 * @author philipe
 */
public class XmlParser {
    
    private static Map<Class, CompositeParser> parsers = new HashMap<Class, CompositeParser>(); 
    
    public static final String DELIMITER = "-";
    
    
    private static <T> CompositeParser<T> getParser(Class<T> cl){
        CompositeParser<T> parser = parsers.get(cl);
        if(parser == null){
            parser = generateParser(cl);
        }
        return parser;
    }
    
    
    public static <T> void parseList(InputStream is, Class<T> cl, Callback<T> callback){
        CompositeParser<T> parser = getParser(cl);
        parse(is, new CallbackParser<T>(fromCamelCase(cl.getSimpleName()), parser, callback));
    }
    
    public static <T> T parse(InputStream is, Class<T> cl){
        CompositeParser<T> parser = getParser(cl);
        return parse(is, parser);
    }
    
    public static <T> T parse(InputStream is, Parser parser){
        try{
            RootParser rootParser = new RootParser(parser);
            final Context context = new Context(rootParser);

            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser sax = factory.newSAXParser();
            
            sax.parse(is, new DefaultHandler(){

                @Override
                public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
                    context.parsers.peek().start(context, qName);
                }

                @Override
                public void characters(char[] ch, int start, int length) throws SAXException {
                    context.parsers.peek().parse(context, new String(ch, start, length));
                }
                

                @Override
                public void endElement(String uri, String localName, String qName) throws SAXException {
                    context.parsers.peek().end(context);
                }
                
                
                
            });

            Root root = (Root)context.objects.peek();
            return (T)root.root;
        }catch(Exception e){
            throw new RuntimeException(e);
        }
    } 
       
  
    public static <T> CompositeParser<T> generateParser(Class<T> cl){
        CompositeParser<T> parser = new CompositeParser<T>(cl);
        Field[] fs = cl.getFields();
        for(Field f : cl.getFields()){
            if(Modifier.isStatic(f.getModifiers())){
                continue;
            }
            Class fc = f.getType();
            Parser p;
            if(fc.isArray()){
                Class c = fc.getComponentType();
                p = new ListParser(fromCamelCase(c.getSimpleName()), generateParser(c));
            } else{
                if(fc == String.class){
                    p = new StringParser();
                }else if(fc == Integer.class){
                    p = new IntParser();
                } else if(fc == Timestamp.class){
                    p = new TimestampParser();
                } else if(fc == Double.class){
                    p = new DoubleParser();
                } else if(fc == Boolean.class){
                    p = new BooleanParser();                
                } else if(fc == Date.class){
                    p = new DateParser();
                } else{
                    p = generateParser(fc);
                }
            }
            parser.add(fromCamelCase(f.getName()), p);
        }
        return parser;
    }

    private static String fromCamelCase(String simpleName) {
        return CamelCase.fromCamelCase(simpleName, DELIMITER);
    }
    
    
    
    
}
